/**
 * raw_dec.c
 *
 *  Created on: 2011-3-28
 *      Author: Lijinxin
 *      Copyright: Ѹ�����
 */

#include "ekk_decoder/raw_decoder_proxy_implement.h"

ekk_bool raw_dec_init(DECODER_INSERT_FUNCS* insert_io_funcs)
{
	mp4_init();
	//InitConvertTable();
	return true;
}

void raw_dec_uninit()
{	
	//DestroyConvertTable();
}

DEC_HANDLE raw_dec_create(DECODER_FORMAT fmt,char* url)
{
	DEC_CONTEXT* p = (DEC_CONTEXT*)ekk_malloc(sizeof(DEC_CONTEXT));
	ekk_memset(p,0,sizeof(DEC_CONTEXT));
	p->fmt = fmt;
	p->dst_pic = avcodec_alloc_frame();
	p->pix_fmt = PIX_FMT_NONE;
	return (DEC_HANDLE)p;
}

void raw_dec_close(DEC_HANDLE dc)
{
	DEC_CONTEXT* p = (DEC_CONTEXT*)dc;
	if(!p)
		return;
	switch(p->fmt)
	{
		case DF_MP4:
			mp4_close((MP4_AVC_CONTEXT*)p->dec_context);
			break;
		case DF_FLV:
			flv_close((FLV_AVC_CONTEXT*)p->dec_context);
			break;
		case DF_NONE:
			break;
	}
}

void raw_dec_destroy(DEC_HANDLE dc)
{
	DEC_CONTEXT* p = (DEC_CONTEXT*)dc;
	if(!p)
		return;
	if(p->dst_pic)
		av_free(p->dst_pic);
	ekk_free(p);
}

DECODER_FORMAT raw_dec_get_decoder_format(DEC_HANDLE dc)
{
	DEC_CONTEXT* p = (DEC_CONTEXT*)dc;
	return p->fmt;
}

int raw_dec_get_info_offset(DEC_HANDLE dc,unsigned char* head,int len)
{	
	DEC_CONTEXT* p = (DEC_CONTEXT*)dc;
	switch(p->fmt)
	{
		case DF_MP4://mp4 file
			p->front_part = head;
			p->front_len = len;
			return mp4_get_info_offset(head,len);
			break;
		case DF_FLV://flv file
			return 0;//0 for the very begining
			break;
		case DF_NONE://unknown
			return false;
	}
	//p->data = (unsigned char*)ekk_malloc()
	return true;
}

ekk_bool raw_dec_analyze_info(DEC_HANDLE dc,unsigned char* raw,int len)
{
	DEC_CONTEXT* p = (DEC_CONTEXT*)dc;
	MP4_AVC_CONTEXT* pmp4 = 0;
	FLV_AVC_CONTEXT* pflv = 0;
	switch(p->fmt)
	{
		case DF_MP4://mp4 file
			pmp4 = mp4_analyze(raw,len,p->front_part,p->front_len);
			if(!pmp4)
				return false;
			p->dec_context = (void*)pmp4;
			p->len_by_sec = pmp4->len_sec;
			p->pos_by_sec = 0;
			p->width = pmp4->width;
			p->height = pmp4->height;
			p->fps = pmp4->fps;
			p->sample_rate = pmp4->sample_rate;
			p->n_channels = pmp4->n_channels;
			switch(pmp4->c->pix_fmt)
			{
				case PIX_FMT_YUV420P:
				case PIX_FMT_YUVJ420P:
					p->fvt = EKK_YUV420;
					break;
				case PIX_FMT_RGB24:
					p->fvt = EKK_RGB24;
					break;
				case PIX_FMT_BGR24:
					p->fvt = EKK_BGR24;
					break;
				default:
					p->fvt = EKK_NONE;
					break;
			}
			p->front_len = 0;
			p->front_part = 0;
			break;
		case DF_FLV://flv file
			pflv = flv_analyze(raw,len);
			if(!pflv)
				return false;
			p->dec_context = (void*)pflv;
			p->len_by_sec = pflv->len_sec;
			p->pos_by_sec = 0;
			p->width = pflv->width;
			p->height = pflv->height;
			p->fps = 0.f;
			p->sample_rate = pflv->sample_rate;
			p->n_channels = pflv->n_channels;
			switch(pflv->c->pix_fmt)
			{
				case PIX_FMT_YUV420P:
				case PIX_FMT_YUVJ420P:
					p->fvt = EKK_YUV420;
					break;
				case PIX_FMT_RGB24:
					p->fvt = EKK_RGB24;
					break;
				case PIX_FMT_BGR24:
					p->fvt = EKK_BGR24;
					break;
				default:
					p->fvt = EKK_NONE;
					break;
			}
			p->front_len = 0;
			p->front_part = 0;
			break;
		case DF_NONE://unknown
			return false;
	}
	//p->data = (unsigned char*)ekk_malloc()
	return true;
}

EKK_VIDEO_FRAME_TYPE raw_dec_get_video_frame_type(DEC_HANDLE dc)
{
	DEC_CONTEXT* p = (DEC_CONTEXT*)dc;
	return p->fvt;
}

int raw_dec_get_width(DEC_HANDLE dc)
{
	DEC_CONTEXT* p = (DEC_CONTEXT*)dc;
	return p->width;
}

int raw_dec_get_height(DEC_HANDLE dc)
{
	DEC_CONTEXT* p = (DEC_CONTEXT*)dc;
	return p->height;
}

int raw_dec_get_frame_per_second(DEC_HANDLE dc)
{	
	DEC_CONTEXT* p = (DEC_CONTEXT*)dc;
	return p->fps;
}

int raw_dec_get_sample_rate(DEC_HANDLE dc)
{
	DEC_CONTEXT* p = (DEC_CONTEXT*)dc;
	return p->sample_rate;
}

int raw_dec_get_num_of_channels(DEC_HANDLE dc)
{
	DEC_CONTEXT* p = (DEC_CONTEXT*)dc;
	return p->n_channels;
}

long long raw_dec_get_length_by_seconds(DEC_HANDLE dc)
{
	DEC_CONTEXT* p = (DEC_CONTEXT*)dc;
	return (long long)p->len_by_sec;
}

long long raw_dec_get_position_by_seconds(DEC_HANDLE dc)
{
	DEC_CONTEXT* p = (DEC_CONTEXT*)dc;
	return (long long)p->pos_by_sec;
}

long long raw_dec_seek(DEC_HANDLE dc,long long second)
{
	DEC_CONTEXT* p = (DEC_CONTEXT*)dc;
	MP4_AVC_CONTEXT* pmp4 = 0;
	FLV_AVC_CONTEXT* pflv = 0;
	int ret_val = 0;
	switch(p->fmt)
	{
		case DF_MP4://mp4 file
			pmp4 = (MP4_AVC_CONTEXT*)p->dec_context;
			if(!pmp4)
				break;
			mp4_seek(pmp4,(int)second);
			p->pos_by_sec = (pmp4->base_ts + pmp4->ts)/1000000;
			break;
		case DF_FLV://flv file
			pflv = (FLV_AVC_CONTEXT*)p->dec_context;
			if(!pflv)
				break;
			flv_seek(pflv,(int)second);
			p->pos_by_sec = (pflv->base_ts + pflv->ts)/1000000;
			break;
		case DF_NONE://unknown
			break;
	}
	return (long long)p->pos_by_sec;
}

int raw_dec_get_next_frame_offset(DEC_HANDLE dc)
{
	DEC_CONTEXT* p = (DEC_CONTEXT*)dc;
	MP4_AVC_CONTEXT* pmp4 = 0;
	FLV_AVC_CONTEXT* pflv = 0;
	int ret_val = 0;
	switch(p->fmt)
	{
		case DF_MP4://mp4 file
			pmp4 = (MP4_AVC_CONTEXT*)p->dec_context;
			if(!pmp4)
				break;
			return mp4_get_next_frame_offset(pmp4);
			break;
		case DF_FLV://flv file
			pflv = (FLV_AVC_CONTEXT*)p->dec_context;
			if(!pflv)
				break;
			return flv_get_next_frame_offset(pflv);		
			break;
		case DF_NONE://unknown
			break;
	}
	return 0;
}

int raw_dec_get_next_frame_size(DEC_HANDLE dc)
{	
	DEC_CONTEXT* p = (DEC_CONTEXT*)dc;
	MP4_AVC_CONTEXT* pmp4 = 0;
	FLV_AVC_CONTEXT* pflv = 0;
	int ret_val = 0;
	switch(p->fmt)
	{
		case DF_MP4://mp4 file
			pmp4 = (MP4_AVC_CONTEXT*)p->dec_context;
			if(!pmp4)
				break;
			return mp4_get_next_frame_size(pmp4);
			break;
		case DF_FLV://flv file
			pflv = (FLV_AVC_CONTEXT*)p->dec_context;
			if(!pflv)
				break;
			return flv_get_next_frame_size(pflv);	
			break;
		case DF_NONE://unknown
			break;
	}
	return 0;
}

DECODER_FRAME_TYPE raw_dec_decode_next_frame(DEC_HANDLE dc,unsigned char* buf,long long* timestamp)
{
	DEC_CONTEXT* p = (DEC_CONTEXT*)dc;
	MP4_AVC_CONTEXT* pmp4 = 0;
	FLV_AVC_CONTEXT* pflv = 0;
	int ret_val = 0;
	switch(p->fmt)
	{
		case DF_MP4://mp4 file
			pmp4 = (MP4_AVC_CONTEXT*)p->dec_context;
			if(!pmp4)
				break;
			ret_val = mp4_decode(pmp4,buf);
			switch(ret_val)
			{
				case IS_AUDIO:
					p->data = pmp4->aud_buffer;
					p->decoded_len = pmp4->aud_decoded_len;
					return DFT_AUDIO;
					break;
				case IS_VIDEO:
					switch(p->fvt)
					{
						case EKK_YUV420:
							if(pmp4->is_avc)
							{
								p->decoded_len = (p->width * p->height) + 2*(p->width/2*p->height/2);
							}
							break;
						case EKK_RGB24:
						case EKK_BGR24:
							if(pmp4->is_avc)
							{
								p->decoded_len = pmp4->picture->linesize[0]*p->height*3;
							}
							break;
						case EKK_NONE:
							return DFT_NONE;
						
					}
					*timestamp = (long long)pmp4->ts;
					p->pos_by_sec = (pmp4->base_ts + pmp4->ts)/1000000;
					return DFT_VIDEO;
					break;
				case IS_NONE:
					return DFT_EOF;
					break;
			}
			break;
		case DF_FLV://flv file
			pflv = (FLV_AVC_CONTEXT*)p->dec_context;
			if(!pflv)
				break;
			ret_val = flv_decode(pflv,buf);
			switch(ret_val)
			{
				case IS_AUDIO:
					p->data = pflv->aud_buffer;
					p->decoded_len = pflv->aud_decoded_len;
					return DFT_AUDIO;
					break;
				case IS_VIDEO:
					switch(p->fvt)
					{
						case EKK_YUV420:
							if(pflv->is_avc)
							{
								//p->data = (unsigned char*)pmp4->picture->data;
								p->decoded_len = (p->width * p->height) + 2*(p->width/2*p->height/2);
							}
							break;
						case EKK_RGB24:
						case EKK_BGR24:
							if(pflv->is_avc)
							{
								//p->data = pmp4->picture->data[0];
								p->decoded_len = pflv->picture->linesize[0]*p->height*3;
							}
							break;
						case EKK_NONE:
							return DFT_NONE;
						
					}
					*timestamp = (long long)pflv->ts;
					p->pos_by_sec = (pflv->base_ts + pflv->ts)/1000000;
					return DFT_VIDEO;
					break;
				case IS_NONE:
					return DFT_EOF;
					break;
			}
			break;
		case DF_NONE://unknown
			break;
	}
	return DFT_NONE;
}

int raw_dec_get_video_frame(DEC_HANDLE dc,unsigned char* buf,EKK_VIDEO_FRAME_TYPE fvt)
{
	DEC_CONTEXT* p = (DEC_CONTEXT*)dc;
	MP4_AVC_CONTEXT* pmp4 = 0;
	FLV_AVC_CONTEXT* pflv = 0;
	unsigned char* src;
	int i,j,w,h,lsz;
	enum PixelFormat pix_fmt;
	switch(p->fmt)
	{
		case DF_MP4://mp4 file
			pmp4 = (MP4_AVC_CONTEXT*)p->dec_context;
			if( fvt == p->fvt )
			{
				switch(fvt)
				{
					case EKK_YUV420:
						if(pmp4->is_avc)
						{
							w = p->width;
							h = p->height;
							lsz = pmp4->picture->linesize[0];
							src = pmp4->picture->data[0];
							for(i = 0;i<h;++i)
							{
								ekk_memcpy(buf,src,w);
								src += lsz;
								buf += w;
							}
							w /= 2;
							h /= 2;
							for(j = 1;j<3;++j)
							{
								lsz = pmp4->picture->linesize[j];
								src = pmp4->picture->data[j];
								for(i = 0;i<h;++i)
								{
									ekk_memcpy(buf,src,w);
									src += lsz;
									buf += w;
								}
							}
						}
						break;
					case EKK_RGB24:
					case EKK_BGR24:
						if(pmp4->is_avc)
						{
							ekk_memcpy(buf,pmp4->picture->data[0],pmp4->picture->linesize[0]*p->height);
						}
						break;
				}
				return p->decoded_len;
			}
			else
			{
				if(p->fvt == EKK_YUV420&&(fvt == EKK_BGR24||fvt == EKK_RGB24))
				{
					if(fvt == EKK_BGR24)
						pix_fmt = PIX_FMT_BGR24;
					else
						pix_fmt = PIX_FMT_RGB24;
					if(p->dst_buf != buf || p->pix_fmt != pix_fmt)
					{
						avpicture_fill((AVPicture *)p->dst_pic,buf, pix_fmt,
										pmp4->c->width, pmp4->c->height);
						p->dst_buf = buf;
						p->pix_fmt = pix_fmt;
					}
					img_convert((AVPicture*)p->dst_pic,pix_fmt,(AVPicture*)pmp4->picture,pmp4->c->pix_fmt,p->width,p->height);
					lsz = p->width * p->height * 3;
				}
				else if((fvt == EKK_BGR24||p->fvt == EKK_RGB24)||(p->fvt == EKK_BGR24||fvt == EKK_RGB24))
				{
					lsz = pmp4->picture->linesize[0]*p->height;
					ekk_memcpy(buf,pmp4->picture->data[0],lsz);
					RGB2BGR_24BIT(buf,lsz);
				}
				return lsz;
			}	
			break;
		case DF_FLV://flv file
			pflv = (FLV_AVC_CONTEXT*)p->dec_context;
			if( fvt == p->fvt )
			{
				switch(fvt)
				{
					case EKK_YUV420:
						if(pflv->is_avc)
						{
							w = p->width;
							h = p->height;
							lsz = pflv->picture->linesize[0];
							src = pflv->picture->data[0];
							for(i = 0;i<h;++i)
							{
								ekk_memcpy(buf,src,w);
								src += lsz;
								buf += w;
							}
							w /= 2;
							h /= 2;
							for(j = 1;j<3;++j)
							{
								lsz = pflv->picture->linesize[j];

								src = pflv->picture->data[j];
								for(i = 0;i<h;++i)
								{
									ekk_memcpy(buf,src,w);
									src += lsz;
									buf += w;
								}
							}
						}
						break;
					case EKK_RGB24:
					case EKK_BGR24:
						if(pflv->is_avc)
						{
							ekk_memcpy(buf,pflv->picture->data[0],pflv->picture->linesize[0]*p->height);
						}
						break;
				}
				return p->decoded_len;
			}
			else
			{
				if(p->fvt == EKK_YUV420&&(fvt == EKK_BGR24||fvt == EKK_RGB24))
				{
					if(fvt == EKK_BGR24)
						pix_fmt = PIX_FMT_BGR24;
					else
						pix_fmt = PIX_FMT_RGB24;
					if(p->dst_buf != buf || p->pix_fmt != pix_fmt)
					{
						avpicture_fill((AVPicture *)p->dst_pic,buf, pix_fmt,
										pflv->c->width, pflv->c->height);
						p->dst_buf = buf;
						p->pix_fmt = pix_fmt;
					}
					img_convert((AVPicture*)p->dst_pic,pix_fmt,(AVPicture*)pflv->picture,pflv->c->pix_fmt,p->width,p->height);
					lsz = p->width * p->height * 3;
				}
				else if((fvt == EKK_BGR24||p->fvt == EKK_RGB24)||(p->fvt == EKK_BGR24||fvt == EKK_RGB24))
				{
					lsz = pflv->picture->linesize[0]*p->height;
					ekk_memcpy(buf,pflv->picture->data[0],lsz);
					RGB2BGR_24BIT(buf,lsz);
				}
				return lsz;
			}
			break;
		case DF_NONE://unknown
			break;
	}
	return 0;
}

int raw_dec_get_audio_frame(DEC_HANDLE dc,unsigned char* buf)
{
	DEC_CONTEXT* p = (DEC_CONTEXT*)dc;
	MP4_AVC_CONTEXT* pmp4 = 0;
	FLV_AVC_CONTEXT* pflv = 0;
	unsigned char* src;
	switch(p->fmt)
	{
		case DF_MP4://mp4 file
			pmp4 = (MP4_AVC_CONTEXT*)p->dec_context;
			ekk_memcpy(buf,pmp4->aud_buffer,pmp4->aud_decoded_len);
			return p->decoded_len;
			break;
		case DF_FLV://flv file
			pflv = (FLV_AVC_CONTEXT*)p->dec_context;
			ekk_memcpy(buf,pflv->aud_buffer,pflv->aud_decoded_len);
			return p->decoded_len;
			break;
		case DF_NONE://unknown
			break;
	}
	return 0;
}

int raw_dec_get_decoded_frame_length(DEC_HANDLE dc)
{
	DEC_CONTEXT* p = (DEC_CONTEXT*)dc;
	return p->decoded_len;
}

ekk_bool raw_dec_enable_skip_frame(DEC_HANDLE dc, DECODER_SKIP_FRAME_LEVEL level)
{
	DEC_CONTEXT* p = (DEC_CONTEXT*)dc;
	p->skip_frame_level = level;
	return level;
}

DECODER_SKIP_FRAME_LEVEL raw_dec_get_skip_frame_level(DEC_HANDLE dc)
{
	DEC_CONTEXT* p = (DEC_CONTEXT*)dc;
	return p->skip_frame_level;
}

long long raw_dec_get_keytimes_by_filepos(DEC_HANDLE dc, unsigned long filepos)
{
	//TODO: need to be implemented....
	return 0;
}

void raw_dec_set_blocked_start_func(DEC_HANDLE dc,dec_blocked_start_func_ptr func,void* param)
{
	//TODO: need to be implemented....
	return;
}

void raw_dec_set_blocked_end_func(DEC_HANDLE dc,dec_blocked_end_func_ptr func,void* param)
{
	//TODO: need to be implemented....
	return;
}

int raw_dec_get_buffered_percents(DEC_HANDLE dc)
{
	//TODO: need to be implemented....
	return 0;
}

ekk_bool raw_dec_do_test(char* filepath, int test_time_sec, ekk_bool enable_yuv_convert, int* fps)
{
	//TODO: NEED TO BE IMPLEMENTED...
	return ekk_false;
}
